package console

import (
	"fmt"
	"leaf/chanrpc"
	"leaf/log"
	"leaf/network"
	"math"
	"os"
	"sort"
	"sync"
)

var (
	server        *network.TCPServer
	sorted        []Command
	maxNameLength int
	commands      = make(map[string]Command)
	mutex         sync.Mutex
)

func Init(closeSignal chan<- os.Signal, port int, profilePath string) {
	mutex.Lock()
	defer mutex.Unlock()

	if port == 0 {
		return
	}

	log.Release("Console port: %d", port)

	server = new(network.TCPServer)
	server.Addr = fmt.Sprintf("localhost:%d", port)
	server.MaxConnNum = int(math.MaxInt32)
	server.PendingWriteNum = 100
	server.NewAgent = newAgent

	server.Start()

	closeCommand := new(closeCommand)
	closeCommand.CloseSignal = closeSignal

	cpuprofCommand := new(cpuprofCommand)
	cpuprofCommand.ProfilePath = profilePath

	profCommand := new(profCommand)
	profCommand.ProfilePath = profilePath

	commands := []Command{
		closeCommand,
		cpuprofCommand,
		profCommand,
		new(helpCommand),
		new(quitCommand),
	}

	for _, item := range commands {
		add(item)
	}
	sortIndexes()
}

func Destroy() {
	if server != nil {
		server.Close()
	}
}

// you must call the function before calling console.Init
// goroutine not safe
func Register(name string, help string, f interface{}, server *chanrpc.Server) {
	mutex.Lock()
	defer mutex.Unlock()

	if _, exists := commands[name]; exists {
		log.Fatal("command %v is already registered", name)
	}

	server.Register(name, f)

	command := new(userCommand)
	command.name = name
	command.help = help
	command.server = server

	add(command)
	sortIndexes()
}

func add(command Command) {
	name := command.Name()
	commands[name] = command
	sorted = append(sorted, command)
	if maxNameLength < len(name) {
		maxNameLength = len(name)
	}
}

func sortIndexes() {
	sort.SliceStable(sorted, func(i int, j int) bool {
		return sorted[i].Name() < sorted[j].Name()
	})
}
